﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson8
{
    #region 多态
    // 概念： 多种状态
    //   让继承同一父类的子类们 在执行相同方法时由不同的表现（状态）
    // 主要目的
    //   同一父类的对象 执行相同行为（方法）有不同表现
    // 解决问题
    //   让同一个对象有唯一行为的特征

    class Father
    {
        public void SpeakMethod()
        {
            Console.WriteLine("Father的方法");
        }
    }

    class Son:Father
    {
        public new void SpeakMethod()
        {
            Console.WriteLine("Son的方法");
        }
    }

    // 多态的实现
    // 编译时的多态 -- 函数重载，开始就写好的
    // 运行时多态 （vob、抽象函数、接口）
    // vob:
    //   v：virtual(虚函数)
    //   o: override(重写)
    //   b: base(父类)
    class Animal
    {
        public string name;

        public Animal(string name)
        {
            this.name = name;
        }

        public virtual void Eat()        // 加了virtual 是虚函数，专门给子类重写的
        {
            Console.WriteLine("吃东西");
        }

    }

    class Tiger : Animal
    {
        public Tiger(string name):base(name)
        {
            
        }

        // 重写虚函数
        public override void Eat()
        {
            // base的作用：代表父类，可以通过base来保留父类的行为
            //base.Eat();
            Console.WriteLine("老虎吃肉");
        }
    }

    #endregion

    #region 抽象类和抽象方法
    // 抽象关键字abstract修饰的类
    //  特点：1、不能被实例化的类  2、可以包含抽象方法  3、继承抽象类必须重写其抽象方法
    abstract class Thing
    {
        // 抽象类中可以用类中的所有知识点
        public string name;
        public abstract void Use();
    }
    class Water : Thing       // 当一个类继承抽象类时，必须实现它的抽象方法，不然报错
    {
        public override void Use()       
        {

        }
    }

    // 抽象函数：又叫纯虚方法  abstract关键字修饰的方法
    //  特点：只能在抽象类中声明  没有方法体   不能是私有的(必须用public修饰)  继承后必须用 override 重写
    abstract class Fruits
    {
        public string name;

        public abstract void Color();  // 纯虚方法  没有方法体，子类必须重写  只能声明再抽象类中

        public virtual void Eat()   // 虚方法  有方法体，子类可以不重写
        {

        }
    }

    class Apple : Fruits
    {
        public override void Color()
        {
            Console.WriteLine("红色苹果");
        }
    }
    #endregion

    #region 接口
    // 概念
    //    接口是行为的抽象规范
    //    它也是一种自定义类型，关键字：interface

    // 接口声明的规范
    //  1、不包含成员变量
    //  2、只包含方法、属性、索引器、事件
    //  3、成员不能被实现
    //  4、成员可以不用写访问修饰符，不能是私有的
    //  5、接口不能继承类，但是可以继承另一个接口

    // 接口的使用规范
    //  类可以继承多个接口    （java中叫实现）
    //  类继承接口后，必须实现接口中所有成员
    // 特点：
    //  它和类的声明类似
    //  接口是用来继承的
    //  接口不能被实例化，但是可以作为容器存储对象

    // 接口的声明
    //   关键字：interface
    // 语法：
    //interface 接口名
    //{

    //}
    // 一句话记忆：接口是抽象行为的“基类”    接口命名规范 帕斯卡前面加个 I
    interface IFly
    {
        //int a;    // 不能写成员变量
        // 可以不写访问修饰符，写的话只能是 public和protected 不能是私有的private
        void Fly();  // 方法

        string Name { get; }   // 属性

        // 索引器
        int this[int index] { get; set; }

        // 事件
        event EventHandler Changed;
    }

    // 接口的使用
    // 接口是用来继承的
    class Weapon
    {

    }

    class Warplane : Weapon, IFly  // 继承接口要实现接口中的方法
    {
        // 实现的时候，权限只能更高不能更低，所以都要加public
        public int this[int index] { 
            get 
            {
                return 0;
            }
            set { 
            }
        }

        public string Name
        {
            get;
        }

        public event EventHandler Changed;

        //public void Fly()
        //{

        //}
        public virtual void Fly()  // 写成一个虚函数让子类再去实现
        {

        }
    }

    // 接口继承接口不需要实现，再让类继承接口后,类要实现所有接口中的内容
    interface IWalk
    {
        void Walk();
    }
    interface IMove:IWalk, IFly
    {

    }
    class Test : IMove
    {
        public int this[int index] { get { return 0; } set { } }

        public string Name
        {
            get;
        }

        public event EventHandler Changed;

        public void Fly()
        {
            
        }

        public void Walk()
        {
            
        }
    }

    // 显示实现接口
    // 当一个类继承两个接口
    // 但是接口中存在同名方法时
    // 注意：显示实现接口时，不能写方法修饰符
    //   写法 接口名.方法名
    interface INormalAtk
    {
        void Atk();
    }
    interface ISkillAtk
    {
        void Atk();
    }
    class Player:INormalAtk, ISkillAtk
    {
        //public void Atk()   // 这样写失去了各自的行为
        //{

        //}
        // 用显示实现接口
        void INormalAtk.Atk()
        {

        }
        void ISkillAtk.Atk()
        {

        }
    }


    #endregion

    #region 密封方法
    // 概念：用密封关键字 sealed 修饰的重写函数
    // 作用：让虚方法或者抽象方法之后不能再被重写
    // 特点：和override一起出现
    abstract class Person
    {
        public string name;

        // 纯虚方法
        public abstract void Eat();

        // 虚方法
        public virtual void Sleep()
        {
            Console.WriteLine("睡觉");
        }

        public virtual void Speak()
        {
            Console.WriteLine("说话");
        }
    }
    class Student : Person
    {
        override sealed public void Eat()
        {
            Console.WriteLine("吃饭");
        }

        public sealed override void Sleep()  // 密封方法
        {
            Console.WriteLine("学生睡觉");
        }

        override public sealed void Speak()   // sealed必须和override一起
        {
            base.Speak();
        }
    }

    class Teacher : Student
    {
        //public override void Eat()     // 上面加了sealed, 这边就报错了
        //{
        //    base.Eat();
        //}
        //public override void Sleep()  // 上面加了sealed, 这样就不能重写了
        //{
        //    Console.WriteLine("老师睡觉");
        //}
        //public override void Speak()        // 上面加了sealed, 这边就报错了
        //{

        //}
    }

    #endregion


    internal class Program
    {
        static void Main(string[] args)
        {
            Father son = new Son();
            son.SpeakMethod();             // 这里执行的是Father中的SpeakMethod

            (son as Son).SpeakMethod();    // 这里执行的是Son中的SpeakMethod

            // 多态的使用
            Animal animal = new Tiger("波波");
            animal.Eat();                  // 这样就是执行Tiger中的Eat了
            (animal as Tiger).Eat();

            // 抽象类不能被实例化
            //Thing things = new Thing();

            // 继承自抽象类的类可以实例化,并且可以用里氏替换原则，用父类容器装子类
            Thing water = new Water();

            // 接口也遵循里氏替换原则
            IFly f = new Warplane();

            Player player = new Player();
            (player as INormalAtk).Atk();
            (player as ISkillAtk).Atk();
        }
    }

    // 总结：
    // 继承类：是对象间的继承，包括特征行为等等
    // 继承接口： 是行为间的继承，继承接口的行为规范，按照规范去实现内容
    // 由于接口也是遵循里氏替换原则，所以可以用接口容器装对象
    // 这样可以实现装载各种毫无关系但是却有相同行为的对象
}
